# -*- coding: utf-8 -*-
# created on 2016/5/12

from collections import defaultdict
from sympy import solveset, acos, cos
from mathsolver.functions.base import *
from mathsolver.functions.base import objects
from mathsolver.functions.base.base import BaseFunction
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSolve


class Search(BaseFunction):
    def solver(self, *args):
        if isinstance(args[0], BasePointName):
            names = [args[0].value, args[0].sympify()]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        if isinstance(args[0], BasePoint):
            names = [args[0].name]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        if isinstance(args[0], BaseLineName):
            names = [args[0].value, args[0].sympify()]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        if isinstance(args[0], BaseLine):
            names = ["".join(args[0].value)]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        if isinstance(args[0], BaseZhiXian):
            names = [args[0].name]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        if isinstance(args[0], BaseYuan):
            names = [args[0].name]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        if isinstance(args[0], BaseZhiXians):
            names = [tt['name'] for tt in args[0].value]
            value = None
            for name in names:
                if isinstance(name, list):
                    value = self.searchs(name)
                elif "name" in args[0].__class__.__name__:
                    value = self.search(name)
                else:
                    value = self.search(name)
                if isinstance(value, dict):
                    break
                elif value is not None:
                    break
            self.steps.append(["答：", value.printing()])
            self.output.append(value)
            return self
        elif isinstance(args[0], BaseLine):
            names = [args[0].name, args[0].sympify()]
        else:
            names = [args[0].sympify(), args[0].value]
        value = None
        name = ""
        for name in names:
            if isinstance(name, list):
                value = self.searchs(name)
            elif "name" in args[0].__class__.__name__:
                value = self.search(name)
            else:
                value = self.search(name)

            # if not value and isinstance(args[0], BaseVariable):
            #     value = self.search(args[0].value)
            if isinstance(value, dict):
                break
            elif value is not None:
                # if isinstance(value, (Set,list)):
                #     value = {name:value}
                # break
                value = {name: value}
                break

        assert value, "not find %s !" % name
        if isinstance(value, BaseValue):
            output = value
        else:
            if len(value) == 1 and isinstance(list(value.values())[0], FiniteSet):
                key, item = list(value.items())[0]
                item = Union(*[i if isinstance(i, Set) else FiniteSet(i) for i in item])
                value = {key: item}
            output = BaseSymbolValue(value)

        self.steps.append(["答：", output.printing()])
        self.output.append(output)
        return self


class BasicUpdate(BaseFunction):
    def solver(self, *args):
        assert args[0]
        if isinstance(args[0], BaseEq) and not args[0].free_symbols():
            return self
        self.output.extend(args)
        return self


class BelongUpdate(BaseFunction):
    def solver(self, *args):
        assert args[0]
        if not isinstance(args[0], BaseBelong):
            raise Exception('Type Match Error')
        b = args[0]
        symb = b.var
        intl = b.interval
        if intl == 'R':
            pass
        else:
            self.output.append(BaseSymbolValue({symb: intl}))
        return self


class BasicUpdates(BaseFunction):
    def solver(self, *args):
        for obj in args[0].objs:
            self.output.append(obj)
        return self


class IsTransfer(BaseFunction):
    def solver(self, *args):
        if isinstance(args[0], (BasePoly, BaseNumber, BaseVariable)) and isinstance(args[1], (BasePoly, BaseNumber, BaseVariable)):
            poly = args[0].sympify()
            number = args[1].sympify()
            self.steps.append(["由题意得：", "%s = %s" % (new_latex(poly), new_latex(number))])
            self.output.append(BaseEq([poly, number]))
        elif isinstance(args[0], BaseVariable) and isinstance(args[1], BaseSymbolValue):
            variable = args[0].sympify()
            symbol_value = args[1].value
            assert len(symbol_value) == 1
            new_value = {variable: symbol_value.values()[0]}
            self.steps.append(["因为%s是方程的一个根" % new_latex(variable), self.output_answer(new_value)])
            self.output.append(BaseSymbolValue(new_value))
        elif isinstance(args[0], BaseSymbolValue) and isinstance(args[1], BaseValue):
            key, v = list(args[0].value.items())[0]
            if isinstance(v, FiniteSet) and len(v) == 1:
                self.output.append(BaseEq([list(v)[0], args[1].value]))
            else:
                self.output.append(BaseEq([v, args[1].value]))
        elif isinstance(args[0], BaseAny) and isinstance(args[1], BaseDu):
            poly1 = args[0].sympify()
            poly2 = args[1].sympify()
            if poly1.func == acos:
                poly1 = poly1.args[0]
                obj = BaseEq([poly1, cos(poly2.args[0] * pi / 180)])
            else:
                obj = BaseEq([poly1, poly2.args[0] * pi / 180])
            self.steps.append(["由题意得：%s" % obj.printing(), ""])
            self.output.append(obj)
        else:
            raise ValueError("unexpected input")
        return self


class SimpleReturn(BaseFunction):
    def solver(self, *args):
        if isinstance(args[0], BaseSineq):
            self.output.append(BaseEq(args[0].value))
        else:
            self.output.extend(args)
        self.steps.append([args[0].printing(), ""])
        return self


class MultipleQuestion(BaseFunction):
    def solver(self, *args):
        values = args[0].value
        if "setops" in args[1]:
            new_main = args[1].replace("setops", "setop")
            for value in values:
                self.output.append(BaseQuestion(BaseSetOp(value), new_main))
        else:
            new_main = args[1].replace("s", "")
            for value in values:
                self.output.append(BaseQuestion(BasePoly(value), new_main))

        return self


class MultipleCondition(BaseFunction):
    def solver(self, *args):
        items = args[0].value
        for item in items:
            if "sets" in args[1]:
                self.output.append(BaseCondition(BaseSet(item), args[1].replace("sets", "set")))
            else:
                name = args[0].__class__.__name__[:-1]
                obj = getattr(objects, name)
                self.output.append(BaseCondition(obj(item), args[1].replace("s", "")))

        return self


class DoNothing(BaseFunction):
    def solver(self, *args):
        return self


class Filter(BaseFunction):
    def solver(self, *args):
        symbol_value = args[0]
        ineq = args[1].sympify()
        ineq = Rel(ineq[0], ineq[2], ineq[1])

        pass_symbol_value = defaultdict(list)
        pass_pair = []
        for pair in symbol_value.value2pairs():
            if ineq.subs(pair):
                pass_pair.append(pair)
                if len(pair) == 1:
                    key, value = list(pair.items())[0]
                    pass_symbol_value[key].append(value)
                else:
                    syms = pair.keys()
                    values = pair.values()
                    pass_symbol_value[tuple(syms)].append(tuple(values))

        assert len(pass_pair) > 0
        self.steps.append(["因为%s" % new_latex(ineq), self.output_answer(pass_symbol_value)])
        self.output.append(BaseSymbolValue(pass_symbol_value))
        return self


class Merge(BaseFunction):
    def solver(self, *args):
        if isinstance(args[0], BaseEq) and isinstance(args[1], BaseIneq):
            f1, f2 = args[0].sympify()
            ineq = args[1].sympify()

            symbol = ineq[0]
            assert symbol.is_Symbol and symbol in (f1 - f2).free_symbols

            ans = list(solveset(f1 - f2, symbol))
            assert len(ans) == 1
            self.output.append(BaseIneq([ans[0], ineq[1], ineq[2]]))
            return self
        elif isinstance(args[0], BaseSymbolValue) and isinstance(args[1], BaseSymbolValue):
            sym_v1 = args[0].value
            sym_v2 = args[1].value

            inter = set(sym_v1.keys()).intersection(sym_v2.keys())

            symbol_value = dict(sym_v1)
            symbol_value.update(sym_v2)
            for k in inter:
                symbol_value[k] = sym_v1[k] + sym_v2[k]

            self.output.append(BaseSymbolValue(symbol_value))
            return self
        elif isinstance(args[0], BaseEq) and isinstance(args[1], BaseEq):
            self.output.append(BaseEqs([args[0].value, args[1].value]))
            return self


class DaXiaoGuanXi(BaseFunction):
    def solver(self, *args):
        symbol_value = args[0].value
        assert all([len(v) == 1 for v in symbol_value.values()])

        pairs = [(k, v[0]) for k, v in symbol_value.items()]
        pairs = sorted(pairs, key=lambda xx: xx[1])
        self.steps.append(["", "所以%s" % "<".join([new_latex(k) for k, _ in pairs])])
        return self


class Update(BaseFunction):
    def solver(self, *args):
        exprs = args[1]
        flag = args[2]

        candidate = [v for k, v in exprs.items() if flag in k]
        assert len(candidate) == 1
        pairs = args[0].value2pairs()
        f1, f2 = candidate[0]
        f1 = sympify(f1)
        f2 = sympify(f2)

        for pair in pairs:
            assert all([f1.has(k) or f2.has(k) for k in pair.keys()])
            new_f1 = f1.subs(pair)
            new_f2 = f2.subs(pair)
            if flag == "point":
                point = BasePoint([new_f1, new_f2])
                self.output.append(point)
                self.steps.append(["将%s代入，得到坐标：" % self.output_symbol_value(pair), point.printing()])

            elif flag == "eq":
                self.output.append(BaseEq([new_f1, new_f2]))
                self.steps.append(
                    ["将%s代入，得到解析式：" % self.output_symbol_value(pair), self.output_eq([new_f1, new_f2])])

        return self


class NumberCheck(BaseFunction):
    """
    元函数：检测number是否相等
    """

    def solver(self, *args):
        poly1 = args[0].sympify()
        poly2 = args[1].sympify()
        assert not poly1.free_symbols and not poly2.free_symbols
        if str(poly1) == str(poly2):
            answer = True
        else:
            answer = False

        self.output.append(BaseVariable(answer))
        return self


class MiniNum(BaseFunction):
    def solver(self, *args):
        if isinstance(args[0], BaseSymbolValue):
            v = args[0].value
            assert len(v) == 1
            interval = v.values()[0]
            assert isinstance(interval, Interval)
            minum = interval.args[0]
        else:
            raise ValueError("unvalid input")
        self.steps.append(["最小值为", new_latex(minum)])
        return self


class MaxNum(BaseFunction):
    def solver(self, *args):
        if isinstance(args[0], BaseSymbolValue):
            v = args[0].value
            assert len(v) == 1
            interval = v.values()[0]
            assert isinstance(interval, Interval)
            num = interval.args[1]
        else:
            raise ValueError("unvalid input")
        self.steps.append(["最大值为", new_latex(num)])
        return self


class SimpleProveEq(BaseFunction):
    def solver(self, *args):
        eq = args[0].sympify()

        if str(eq[0]) == str(eq[1]):
            self.steps.append(["%s %s 等式成立" % (latex_symbol["because"], args[0].printing()), "得证"])
        else:
            from mathsolver.functions.shuyushi.compute import ShuYuShiCompute
            f = eq[0] - eq[1]
            stepsolver = ShuYuShiCompute(known=self.known).solver(BasePoly(f))
            assert str(stepsolver.output[0].value) == "0" or 0 in stepsolver.output[0].value

            self.steps.append(["%s %s 恒等于 0" % (latex_symbol["because"], new_latex(f)), "得证"])

        return self


class SimpleProveIneq(BaseFunction):
    def solver(self, *args):
        ineq = args[0].sympify()

        if Rel(ineq[0], ineq[2], args[0].value[1]):
            self.steps.append(["%s %s 恒成立" % (latex_symbol["because"], args[0].printing()), "得证"])
        else:
            from mathsolver.functions.shuyushi.compute import ShuYuShiCompute
            f = ineq[0] - ineq[2]
            stepsolver = ShuYuShiCompute(known=self.known).solver(BasePoly(f))
            assert Rel(stepsolver.output[0].value, 0, args[0].value[1])
            self.steps.append(["%s %s 恒成立" % (latex_symbol["because"], args[0].printing()), "得证"])

        return self


class SimpleProveIneqs(BaseFunction):
    def solver(self, *args):
        ineqs = args[0].ineqs
        if args[0].free_symbols():
            for ineq in ineqs:
                if isinstance(ineq, BaseEq):
                    SimpleProveEq(known=self.known).solver(ineq)
                else:
                    SimpleProveIneq(known=self.known).solver(ineq)
        else:
            assert all([Rel(ineq.sympify()[0], ineq.sympify()[2], ineq.value[1]) for ineq in ineqs])
        self.steps.append(["%s %s" % (latex_symbol["because"], args[0].printing()), "得证"])
        return self


class SimpleProveEqs(BaseFunction):
    def solver(self, *args):
        if args[0].free_symbols():
            ans = MathematicaSolve().solver(args[0]).output[0]
            self.steps.append(["%s %s" % (latex_symbol["because"], ans.printing()), "得证"])
        else:
            assert all([Eq(f1, f2) is True for f1, f2 in args[0].sympify()])
            self.steps.append(["%s %s" % (latex_symbol["because"], args[0].printing()), "得证"])
        return self


class SimpleProveSymbolValue(BaseFunction):
    def solver(self, *args):
        assert len(args[0].value) == 1
        key, value = list(args[0].value.items())[0]
        assert value == S.Reals
        self.steps.append(["%s %s" % (latex_symbol["because"], args[0].printing()), "得证"])
        return self


class VariableIsCondition(BaseFunction):
    def solver(self, *args):
        v = args[0].sympify()
        if "正" in args[1]:
            self.output.append(BaseSymbolValue({v: S.Integers.intersection(Interval(0, S.Infinity, True))}))
        elif "非负" in args[1]:
            self.output.append(BaseSymbolValue({v: S.Integers.intersection(Interval(0, S.Infinity))}))
        elif "负" in args[1]:
            self.output.append(BaseSymbolValue({v: S.Integers.intersection(Interval(-S.Infinity, 0, False, True))}))
        else:
            self.output.append(BaseSymbolValue({v: S.Integers}))

        return self


class SimpleGeShu(BaseFunction):
    def solver(self, *args):
        value = args[0]
        self.steps.append(["个数为%s" % len(value.value), ""])
        return self


class SimpleSum(BaseFunction):
    def solver(self, *args):
        v = args[0].sympify() + args[1].sympify()
        self.output.append(BasePoly(v))
        return self


if __name__ == '__main__':
    pass
